---
title: Application security and authorization
summary: Consolidated authorization architecture, AD resource catalog, security enforcement summary, and data-level access control across LegalMaintenance.
status: draft
owner: SAIF Doc Gen
last_reviewed: 2026-08-21
---

# Application security and authorization

This page documents the application-wide authorization architecture for LegalMaintenance, consolidating authorization patterns from all libraries and the front-end layer. Authentication is handled entirely by infrastructure (NTLM/AD) and is not application-level — all code receives a `WindowsIdentity` on the request context.

## Authentication architecture

Authentication is handled entirely by infrastructure and is not part of application code. All requests arrive with a `WindowsIdentity` established by IIS via NTLM/Windows-integrated authentication (`<authentication mode="Windows" />`); the application has no code for the authentication mechanism itself.

## Authorization architecture

All authorization in this application resolves to a single primitive: `clsSecurityPrincipal.IsAuthorized(resourceName)`, supplied by the `SAIF.BOF.Framework` NuGet package (`SAIF.Framework.Security`). The web tier never names an AD group directly — it names a base resource name, and the framework appends an environment suffix (`Resource` in production, `TestResource` in non-production) before performing the AD group-membership lookup.* The `clsSecurityPrincipal` travels from `Model.Context.SecurityPrincipal` (session-held view state) → manager constructor → `LegalManagerBase.SecurityPrincipal` → `IsAuthorized`; the web tier never constructs or mutates a principal.

```mermaid
flowchart TD
    User[User Request] --> IIS[IIS NTLM Authentication]
    IIS --> Identity[WindowsIdentity Established]
    Identity --> Manager["LegalManagerBase-derived<br>Manager"]
    Manager --> Principal[clsSecurityPrincipal]
    Principal --> IsAuth[IsAuthorized resourceName]
    IsAuth --> AD[("Active Directory group<br>membership")]
    AD --> Result[Access Decision]
```

## AD resource catalog

| Resource name | Purpose | Libraries using | Enforcement type |
| --- | --- | --- | --- |
| `LNView` | Legal view-only membership; when the principal **is** in the group, editing is suppressed | [SAIF.Application.Claim.Legal](../libraries/saif-application-claim-legal/overview.md) | Programmatic (`LegalManagerBase.IsInViewOnly`) — the only resource reached by LegalMaintenance web-tier code (6 call sites) |
| `LNAdmin` | Legal administrator; bypasses claim conflict/exposure gating in manager retrieve logic | [SAIF.Application.Claim.Legal](../libraries/saif-application-claim-legal/overview.md) | Programmatic (`LegalManagerBase.IsInAdminGroup`) — no direct screen call, reached transitively |
| `LNCaseUpdate` | Legal case update authority | [SAIF.Application.Claim.Legal](../libraries/saif-application-claim-legal/overview.md) | Programmatic (`LegalManagerBase.IsInCaseUpdateGroup`) — not consumed by LegalMaintenance; used by sibling LegalNet case managers |
| `LNConflict` | Visibility of worker-conflict (confidential) claims | [SAIF.Application.Claim.Legal](../libraries/saif-application-claim-legal/overview.md) | Programmatic (`LegalManagerBase.HasLegalConflictAccess`) — manager-internal, no direct screen call |
| `LNExposure` | Visibility of exposure / health-issue claims | [SAIF.Application.Claim.Legal](../libraries/saif-application-claim-legal/overview.md) | Programmatic (`LegalManagerBase.HasLegalExposureAccess`) — manager-internal, no direct screen call |
| `ConflictClaim` | Claim-level conflict access token | [SAIF.Application.Claim](../libraries/saif-application-claim/overview.md) | Programmatic (`ClaimUserAccess.ConflictAccess` → `UserAccess.HasAccess`) — requires a claim context, which LegalMaintenance never sets |
| `LNNoteView` | Legal case-note read access | [SAIF.Application.Claim.Legal](../libraries/saif-application-claim-legal/overview.md) | Programmatic (`clsCaseNotesManager`) — LegalNet application only, not LegalMaintenance |
| `LNUpdate` | Legal case-note update access | [SAIF.Application.Claim.Legal](../libraries/saif-application-claim-legal/overview.md) | Programmatic (`clsCaseNotesManager`) — LegalNet application only |
| `LNLCEUpdate` | Legal calendar-event update | [SAIF.Application.Claim.Legal](../libraries/saif-application-claim-legal/overview.md) | Programmatic (`clsScheduleManager`) — LegalNet application only |
| `LNDueDateUpdate` | Legal due-date update | [SAIF.Application.Claim.Legal](../libraries/saif-application-claim-legal/overview.md) | Programmatic (`clsScheduleManager`) — LegalNet application only |

## Security enforcement summary

| Layer | Enforcement mechanism | Count | Libraries |
| --- | --- | --- | --- |
| Presentation | No `[SAIFAuthorize]`/`[SaifAuthorize]` attributes found; screens display/hide toolbar state based on manager properties | 0 | LegalMaintenance |
| Business logic | Programmatic `IsAuthorized`/`HasAccess` calls | 10 (13 total resources in reachable hierarchy, 1 actually evaluated by web-tier code) | [SAIF.Application](../libraries/saif-application/overview.md), [SAIF.Application.Claim.Legal](../libraries/saif-application-claim-legal/overview.md), [SAIF.Application.Claim](../libraries/saif-application-claim/overview.md) |
| Data access | Data ownership validation | 1 (confidential-claim/case gating, computed but only `ConflictAccess` hard-blocked) | [SAIF.Application.Claim](../libraries/saif-application-claim/overview.md), [SAIF.Application.Claim.Legal](../libraries/saif-application-claim-legal/overview.md) |
| Infrastructure | SOD checks | 0 | n/a |

## Authorization enforcement points

Every screen builds its manager in `Page_Init` with the identical two-argument pattern `Model.SetActiveManager(New Manager(Model.DatabaseEnvironment, Model.Context.SecurityPrincipal))`. The `clsSecurityPrincipal` travels `Model.Context.SecurityPrincipal` (session-held view state) → manager constructor → `LegalManagerBase.SecurityPrincipal` → `IsAuthorized`; the web tier never constructs or mutates a principal.

| Screen | File | Check | Guard condition | Effect on evaluation `True` |
| --- | --- | --- | --- | --- |
| Attorney Assignment | `frmAttorneyAssignment.aspx.vb` | `Manager.IsInViewOnly` | Inside `If Not IsPostBack`, after `BindObjects()` | `DeleteButton`/`InsertButton`/`SaveButton.Enabled = False` |
| Attorney Profile | `frmAttorneyProfile.aspx.vb` | `Manager.IsInViewOnly` | Inside `If Not IsPostBack`, after `Manager.GetProfiles()` | Delete/Insert/Save disabled |
| Attorney Profile | `frmAttorneyProfile.aspx.vb` | `Manager.IsViewOnly` | Inside `LoadData()`, called from the `Page_Load` first-load branch | `Utilities.clsControlManager.EnablePageControl(Me, False)` — disables every control on the page*, unreachable in practice (see below) |
| Hearing Location | `frmHearingLocation.aspx.vb` | `Manager.IsInViewOnly` | Inside `If Not IsPostBack`, after `LoadData()` + `ApplyPrefs()` | Delete/Insert/Save disabled |
| Legal Person | `frmLegalPerson.aspx.vb` | `Manager.IsInViewOnly` | Inside `If Not IsPostBack` | Delete/Insert/Save disabled |
| Other Firms | `frmOtherFirms.aspx.vb` | `Manager.IsInViewOnly` | Inside `If Not IsPostBack` | Delete/Insert/Save disabled |
| Rotation | `frmRotation.aspx.vb` | `Manager.IsInViewOnly` | Inside `If Not IsPostBack`, after `LoadData()` | Delete/Insert/Save disabled |

**Total enforcement points in the web project: 7** (6 × `IsInViewOnly` + 1 × `IsViewOnly`). Every `IsInViewOnly` block is a pure demotion of a toolbar that is set enabled immediately beforehand.

Pages with zero authorization enforcement: `Default` (redirect-only landing page), `frmPrintPreview` (streams a generated PDF; relies on the caller having already produced the report into the session model plus possession of the `__SAIFPID` view ID, with no resource check), `SessionPing` (keep-alive only), and `DataHelper` (see below).

`frmAttorneyProfile`'s `IsViewOnly` gate reads `AppManagerBase.SecurityRole`, which defaults to `AppSecurityRole.NoAccess` and is mutated only via `ClaimAppManager.OnCheckSecurity`, reachable only through `SetClaimContext`. No LegalMaintenance code-behind calls `SetClaimContext`, so `SecurityRole` remains `NoAccess` for the lifetime of a LegalMaintenance manager and the whole-page lockdown on Attorney Profile never executes — it is dead code.*

`DataHelper.aspx` (`Inherits System.Web.UI.Page`, bypassing `clsLegalMaintenanceAncestor`) copies every `Request.Form` key/value into a dictionary with no filtering, resolves its manager by reading session directly via an attacker-supplied `ViewID`, and calls `Manager.ProcessAjaxRequest(requestItems)` inside a `Try/Catch` with an empty catch block. It performs no `IsInViewOnly`/`IsViewOnly`/resource check — a principal in the `LNView` group has the toolbar disabled on every screen but is not prevented from posting to this endpoint (see [SEC-APP-003](#sec-app-003-inline-ajax-grid-edit-has-no-authorization-gate) below).

All enforcement is UI-only: no Save/Insert/Delete handler in the web project re-tests authorization before invoking manager mutations, and there is no fail-closed default anywhere in the web tier — the floor of the permission model is a fully enabled toolbar.

## Security interfaces and contracts

| Interface / contract | Type | Purpose | Consumers |
| --- | --- | --- | --- |
| `clsSecurityPrincipal.IsAuthorized(resourceName As String) As Boolean` | Method on `SAIF.Framework.Security` (`SAIF.BOF.Framework` NuGet package) | Single primitive through which all authorization in this application resolves; the framework appends an environment suffix (`Resource` in production, `TestResource` in non-production) to the supplied base resource name before performing the AD group-membership lookup* | `LegalManagerBase.IsInViewOnly`, `LegalManagerBase.IsInAdminGroup`, `LegalManagerBase.IsInCaseUpdateGroup`, `LegalManagerBase.HasLegalConflictAccess`, `LegalManagerBase.HasLegalExposureAccess` |
| `LegalManagerBase.SecurityPrincipal` | Property (constructor-injected) | Carries the `clsSecurityPrincipal` from `Model.Context.SecurityPrincipal` into manager instances; the web tier never constructs or mutates a principal | All `LegalManagerBase`-derived managers |
| `AppSecurityRole` enum (`ViewOnly`, `Update`, `NoAccess`) | Enum + `AppManagerBase.SecurityRole` property | Non-AD, claim-context-driven authorization role; defaults to `NoAccess` and is set only via `ClaimAppManager.OnCheckSecurity` | `AppManagerBase.IsViewOnly`, `IsUpdateable`, `IsRestricted`/`IsAccessible` |
| `ClaimAppManager.OnCheckSecurity()` / `SetClaimContext(inClaimID)` | Method pair | Would populate `AppSecurityRole` and claim-level access tokens (`ConflictAccess`, `ExposureAccess`, `SecureClaimAccess`, `HealthIssueAccess`) for a given claim context | Not invoked by any LegalMaintenance code-behind — this application never sets a claim context |
| `clsAttorneyTableManager.ProcessAjaxRequest(requestItems As Dictionary(Of String, String))` | Method | Entry point for `DataHelper.aspx`'s unauthenticated-at-the-web-tier grid mutation dispatch | `DataHelper.aspx.vb` |
| WCF `SecurityService` (resolved inside `SAIF.BOF.Framework`) | External service | Backing AD group-membership lookup for `IsAuthorized`; no endpoint URL/configuration key is present in this repository** | `clsSecurityPrincipal` (framework-internal) |

## Cross-cutting authorization rules

### SEC-APP-001 AD-group-gated legal access is inconsistently enforced

| Field | Value |
| --- | --- |
| **Statement** | Of the 13 AD resource names in the reachable type hierarchy, exactly one — `LNView` — is evaluated by LegalMaintenance screen code. |
| **Trigger** | Every screen load of a `LegalManagerBase`-derived maintenance screen |
| **Enforcement** | `LegalManagerBase.IsInViewOnly` checks `LNView`; other resources evaluated only transitively inside manager retrieve logic or unused |
| **Enforcing libraries** | [SAIF.Application](../libraries/saif-application/overview.md), [SAIF.Application.Claim.Legal](../libraries/saif-application-claim-legal/overview.md) |
| **Outcome** | Toolbar disabled for `LNView`-authorized users; other resources have no direct enforcement point in this application |
| **Related resources** | `LNView`, `LNAdmin`, `LNCaseUpdate`, `LNConflict`, `LNExposure` |

---

### SEC-APP-002 Confidential-claimcase gating is not active in LegalMaintenance

| Field | Value |
| --- | --- |
| **Statement** | `LegalManagerBase.OnRetrieve` combines AD group membership (`LNAdmin`, `LNConflict`, `LNExposure`) with claim data attributes (`IsWorkerConflict`, `IsExposure`) and a database-backed per-claim override — the canonical SAIF data-level authorization pattern — but this composite gate does not fire in LegalMaintenance because no screen sets a claim context or reads `CanViewClaim`. |
| **Trigger** | Manager retrieve logic (`OnRetrieve`) upon a claim-context change |
| **Enforcement** | `LegalManagerBase.OnRetrieve` composite gate at `LegalManagerBase.vb:334-355` |
| **Enforcing libraries** | [SAIF.Application.Claim](../libraries/saif-application-claim/overview.md), [SAIF.Application.Claim.Legal](../libraries/saif-application-claim-legal/overview.md) |
| **Outcome** | Documented for architectural completeness; not an active enforcement path from LegalMaintenance screens |
| **Related resources** | `LNAdmin`, `LNConflict`, `LNExposure` |

---

### SEC-APP-003 Inline AJAX grid edit has no authorization gate

| Field | Value |
| --- | --- |
| **Statement** | `DataHelper.aspx` bypasses the shared page ancestor and performs no `IsInViewOnly`/`IsViewOnly`/resource check before dispatching to `clsAttorneyTableManager.ProcessAjaxRequest`; a principal in the `LNView` (view-only) group can still mutate data through this path. |
| **Trigger** | Any AJAX POST issued by a maintenance-screen grid to `DataHelper.aspx` |
| **Enforcement** | None — no resource check exists on this endpoint |
| **Enforcing libraries** | [SAIF.Application.Claim.Legal](../libraries/saif-application-claim-legal/overview.md) |
| **Outcome** | View-only users can mutate attorney table data through this endpoint; exceptions are silently swallowed |
| **Related resources** | `LNView` |

---

### SEC-APP-004 `LNView` semantics are inverted relative to its name

| Field | Value |
| --- | --- |
| **Statement** | `IsInViewOnly` returns `True` when the principal **is authorized for** `LNView`, and the screens then disable editing; users in no legal group at all evaluate `IsInViewOnly = False` and get a fully enabled toolbar — the opposite of what a "view" permission name implies, and unconfirmed by any domain SME.* |
| **Trigger** | Every screen load evaluating `IsInViewOnly` |
| **Enforcement** | `LegalManagerBase.IsInViewOnly` |
| **Enforcing libraries** | [SAIF.Application.Claim.Legal](../libraries/saif-application-claim-legal/overview.md) |
| **Outcome** | Users in no legal group at all receive a fully enabled toolbar rather than being locked out, contrary to a least-privilege expectation |
| **Related resources** | `LNView` |

## Data-level authorization

| Entity type | Validation pattern | Enforcement library | Source |
| --- | --- | --- | --- |
| Legal case/claim (confidential) | `ClaimSecurity.CheckClaim(inClaim)` sets `ConflictAccess`/`ExposureAccess`/`HealthIssueAccess`/`SecureClaimAccess` by testing claim attributes against the user's resource tokens; only invoked from `ClaimAppManager.OnCheckSecurity`, which requires `SetClaimContext` (not exercised by LegalMaintenance screens) | [SAIF.Application.Claim](../libraries/saif-application-claim/overview.md) | LegalMaintenance web application (LegalMaintenance/*.aspx.vb) |

## Segregation of duties

No SOD rules exist in the analyzed libraries: no class, method, or constant matching `SegregationOfDuties`/`SOD`/`employee_access`/`menu_structure` was found in `LegalMaintenance` or the directly-referenced `SAIF.Application*` projects, and the five `LN*` resources are tested independently and combined only with `OrElse` (permissive union) rather than a mutual-exclusion restriction.

## Security dependencies

| Dependency | Type | Purpose |
| --- | --- | --- |
| WCF SecurityService / `clsSecurityPrincipal` (`SAIF.BOF.Framework`) | External service / NuGet package | Centralized AD group membership checks via `IsAuthorized(<resourceName>)` |
| Active Directory | Infrastructure | User/group/machine identity store |
| SAIF.Framework.Security | NuGet package | Supplies `clsSecurityPrincipal` and the resource-suffix convention |

## Notes
- AD resource names use base names only — the framework appends `Resource` (production) or `TestResource` (non-production) at runtime.
- The absence of any dual-control or SOD constraint on a master-data maintenance application (attorneys, firms, hearing locations, rotation assignments) is worth surfacing to a domain SME.*

> **Note:** Statements marked with `*` were inferred from code structure, naming conventions, or
> contextual evidence where direct confirmation was not available in source artifacts. Statements
> marked with `**` could not be confirmed; treat as unknown until verified.

## Related documentation
- [Index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Dependencies](dependencies.md)
- [Configuration](configuration.md)
- [Operations and processes](operations-processes.md)
- [Business rules](business-rules.md)
- [Data model](data-model.md)
- [Tech debt](tech-debt.md)

> **Note:** Statements marked with `*` were inferred from code structure, naming
> conventions, or contextual evidence where direct confirmation was not available
> in source artifacts. Statements marked with `**` could not be confirmed; treat
> as unknown until verified.

## Sources
- LegalMaintenance web application (LegalMaintenance/*.aspx.vb)
